var RiseVision = RiseVision || {};
RiseVision.RSS = {};

RiseVision.RSS = function() {
    this.isFeedLoaded = false;
    this.isFirstEntry = true;
    this.groupIndex = 0;
    this.imageCount = 0;
    this.feedLoadFailedInterval = 5000;   //5 seconds
    this.urls = new Array();
    this.images = new Array();
    this.duration = prefs.getInt("duration") * 1000;
    this.layoutURL = prefs.getString("layoutURL") ? prefs.getString("layoutURL") : "http://risegadgets.googlecode.com/svn/test/RSSGadgetCurrent/Default/Default.xml";
    //this.layoutURL = prefs.getString("layoutURL") ? prefs.getString("layoutURL") : "http://b943827a172dab9c23d3-df3a9e96b5067352de9d6a5e8df12018.r0.cf2.rackcdn.com/Default/Default.xml";
    this.startTimeout = new Date();
    this.timeLeft = 0;
    this.isLoading = true;
    this.isPlaying = false;
    this.isPaused = false;
    this.isFadingIn = false;
    this.isFadingOut = false;
    this.supportedTypes = new Array("image/bmp", "image/gif", "image/jpeg", "image/png", "image/tiff");
}
RiseVision.RSS.prototype.initialize = function () {
    var self = this,
	params = {};
 
    if (this.layoutURL) {
	params[gadgets.io.RequestParameters.CONTENT_TYPE] = gadgets.io.ContentType.DOM;
	gadgets.io.makeRequest(this.layoutURL, function(obj) {
	    var data = obj.data;
	    
	    if (data.getElementsByTagName("Style").length > 0) {
		var head = document.getElementsByTagName("head")[0],
		    style = document.createElement("style");
		    
		style.type = "text/css";
		style.innerHTML = data.getElementsByTagName("Style")[0].childNodes[1].nodeValue;
		head.appendChild(style);
	    }
	    
	    if (data.getElementsByTagName("Layout").length == 0) {
		return;
	    }
	    
	    self.entry = data.getElementsByTagName("Layout")[0].childNodes[1].nodeValue;
	    self.feed = new google.feeds.Feed(prefs.getString("murl"));
	    self.feed.setResultFormat(google.feeds.Feed.MIXED_FORMAT);
	    self.feed.setNumEntries(prefs.getInt("totalItems"));
	    self.feed.load(function(result) {
		if (!result.error) {
		    self.isFeedLoaded = true;
		    self.feedData = result.feed;
		    self.showFeed();
		} 
	    });
	}, params);
    }
}
RiseVision.RSS.prototype.loadFeed = function() {
    var self = this;
    
    //Start a timer in case there is a problem loading the feed.
    this.feedLoadFailedTimer = setTimeout(function() {
	self.feedLoadFailed();
    }, this.feedLoadFailedInterval);

    this.feed.load(function(result) {
	//Issue 753 - If there is an error loading the feed, this API call will try to reload it at a later time.
	//Check that feedLoadFailed has not already executed as it would interfere with the execution of the Gadget.
	if (result.error == null && !self.isFeedLoaded) {
	    clearTimeout(self.feedLoadFailedTimer);
	    self.isFeedLoaded = true;
	    self.feedData = result.feed;
	    self.showFeed();
	}
	else if (result.error != null) {	    
	    self.isFeedLoaded = true;
	    
	    if (!self.isPaused) {	
		self.showEntry();
	    }
	    
	    console.log(result.error);
	}
    }); 
}
//This function will be called if the feed fails to load, for example, when there is no Internet connection.
//In that case, continue playing the same feed entries.
RiseVision.RSS.prototype.feedLoadFailed = function() {
    this.isFeedLoaded = true;	//Issue 753
    this.isFadingIn = false;	//Issue 776
    this.isFadingOut = false;	//Issue 776
    
    if (!this.isPaused) {	
	this.showEntry();
    }
}
//This is only called when the feed has loaded or been reloaded.
RiseVision.RSS.prototype.showFeed = function() {
    if ((this.feedData) && (this.feedData.entries) && (this.feedData.entries.length > 0)) {
	this.urls = new Array();	
	
	//Check if there are any images to be displayed with this feed.
	if (prefs.getBool("showMedia")) {
	    this.getImages();
	}
	
	//Preload any images.
	if (this.urls.length > 0) {
	    this.preloadImages();
	}
	else {
	    this.init();
	    this.isFadingIn = false;	//Issue 776
	    this.isFadingOut = false;	//Issue 776
	    
	    if (this.isLoading) {	    		
		this.isLoading = false;
		readyEvent();
	    }
	    else {
		this.showEntry();
	    }
	}
    }
}
RiseVision.RSS.prototype.init = function() {
    var numEntries = this.feedData.entries.length,
	items = prefs.getInt("items"),
	entriesAdded = 0,
	$entries, entry;
    
    if (prefs.getString("transition") == "fade") {
	$(".group").remove();
    }
    else {
	//Remove all slides except the last, since that one is still showing.
	$(".group:not(:last-child)").remove();
    }
    
    if (this.isLoading) {
	$entries = $("<section class='entries'>")
	$entries.appendTo(document.body);
    }
    else {
	$entries = $(".entries")
    }
    
    //Add an article element for each entry.
    for (var i = 0; i < numEntries; i++) {
	//Add an element to group together the appropriate number of items.
	if (entriesAdded % items == 0) {
	    $(".entries").append($("<div class='group'>").hide());
	}
	
	entry = this.feedData.entries[i];
	
	$(".group:last").append(this.entry);	
	
	//Title
	if (prefs.getBool("showTitle")) {
	    $(".title:last a").attr("href", entry.link);
	    $(".title:last a").html(entry.title);
	}
	else {
	    $(".title:last").remove();
	}
	
	//Separator
	if (!prefs.getBool("showSeparator")) {
	    $(".separator:last").remove();
	}
	
	//Show full text or snippet.
	if (prefs.getString("text") == "full") {
	    $(".text:last").html(entry.content);
	}
	else {
	    $(".text:last").html(entry.contentSnippet);
	}
    
	this.renderAuthorAndDate(entry);	
    
	if (this.images[i]) {
	    this.showImage($(".entries .entry:last").find(".text"), this.images[i]);
	}
	
	if (prefs.getString("transition") == "slide") {
	    $(".group:last").addClass("slide");
	    
	    if (i == numEntries - 1) {
		$(".group:last").addClass("last");
	    }
	}
	
	entriesAdded++;
    }
}
RiseVision.RSS.prototype.showEntry = function() {
    var self = this,
	transition = prefs.getString("transition"),
	$group = $(".entries .group").eq(this.groupIndex),
	$oldGroup;

    if (this.groupIndex >= $(".entries .group").length) {
	this.isFeedLoaded = false;
	this.timeLeft = 0;	//Donna
	this.groupIndex = 0;
	doneEvent();
    }
    else {			
	//If the entry at index 0 has a class of last, then $group is the subsequent entry.
	//What about when there is only one entry?
	if ((transition == "slide") && (this.groupIndex == 0) && $group.hasClass("last")) {
	    $group = $(".entries .group").eq(1);
	}
		
	//Auto size the feed entries.
	if (prefs.getBool("autoSize")) {						
	    this.autoSize($group);
	}
	
	//The first time an entry is displayed, it will not transition in. This code will only execute once.
	if (this.isFirstEntry) {
	    if (transition == "fade") {
		this.duration = prefs.getInt("duration") * 1000 - 600;	//600 is fade out only.	    
	    }
	    else {
		this.duration = prefs.getInt("duration") * 1000 - 1200;	//1200 is slide in/out.
	    }
	    
	    $group.addClass("visible").show();
	    
	    this.isFirstEntry = false;
	    this.startTimer();
	    this.groupIndex++;	//Issue 776
	}
	else {
	    this.duration = prefs.getInt("duration") * 1000 - 1200;	//1200 is transition time.
	    
	    if (transition == "fade") {
		//Make damn sure that there are no visible groups at this point in time.
		if ($(".visible").length > 0) {
		    $(".visible").hide();
		    $(".group").removeClass("visible");
		}
		
		if (!this.isFadingIn && !this.isFadingOut) {
		    this.isFadingIn = true;
		    
		    //Start the timer when fade in has completed.
		    $group.fadeIn("slow", function() {
			self.isFadingIn = false;
			$(this).addClass("visible");
			
			if (!self.isPaused) {
			    self.startTimer();
			}
		    });
		    
		    this.groupIndex++;		//Issue 776
		}
	    }
	    else {
		$oldGroup = $(".entries .visible");	    
		
		//Slide old entry out.	    
		$oldGroup.animate({
		    left: -prefs.getInt("rsW") - 10
		}, 1200, function() {
		    $(this).removeClass("visible").hide();
		    
		    //Remove last group from previous cycle.
		    if ($(".entries .entry").length > self.feedData.entries.length) {
			$(this).remove();
		    }		
		});
		
		//Slide new entry in.
		$group.css("left", prefs.getInt("rsW")).show();
		$group.animate({
		    left: 0
		}, 1200, function() {
		    $(this).addClass("visible");
		    
		    if (!self.isPaused) {
			self.startTimer();
		    }
		});
		
		this.groupIndex++;	//Issue 776
	    }
	}
	
	//this.groupIndex++;		//Issue 776
    }
}
RiseVision.RSS.prototype.renderAuthorAndDate = function(entry) {
    var showAuthor = prefs.getString("showAuthor");
    
    if (showAuthor == "none") {
	$(".author:last").remove();    
    }
    else {
	//Alignment
	if ((showAuthor == "headerRight") || (showAuthor == "footerRight")) {
	    $(".author:last").addClass("authorRight");
	}
	else {
	    $(".author:last").addClass("authorLeft");
	}
	
	//Set Author and Date text.
	if (entry.author)
	    $(".author:last").text(new Date(entry.publishedDate).toString("MMMM dS, yyyy") + " by " + entry.author);
	else
	    $(".author:last").text(new Date(entry.publishedDate).toString("MMMM dS, yyyy"));
	    
	//Placement
	if ((showAuthor == "footerRight") || (showAuthor == "footerLeft")) {
	    var $author = $(".author:last").clone();
	    
	    $(".author:last").remove();
	    $(".entries .group:last").append($author);
	}
    }	
}
/* 		Images			*/
RiseVision.RSS.prototype.getImages = function() {
    var numEntries = this.feedData.entries.length,
	imageFound;

    for (var i = 0; i < numEntries; i++) {
	imageFound = false;

	//Add support for enclosures. Enclosures are only returned in the XML result.
	var encs = this.feedData.entries[i].xmlNode.getElementsByTagName("enclosure");
	var links = this.feedData.entries[i].xmlNode.getElementsByTagName("link");
	
	if (encs.length > 0) {	//RSS enclosure
	    //Check if the enclosure is an image (RSS allows only one enclosure per item).
	    for (var j in this.supportedTypes) {
		if (encs[0].getAttribute("type") == this.supportedTypes[j]) {
		    this.urls.push(encs[0].getAttribute("url"));
		    imageFound = true;
		    break;
		}
	    }
	}						
	//else if (this.hasImage(this.feedData.entries[imageIndex])) {	//Media RSS
	else if (this.hasImage(this.feedData.entries[i])) {	//Media RSS
	    //Only the first image is displayed.
	    var url = this.feedData.entries[i].mediaGroups[0].contents[0].url;

	    this.urls.push(url);
	    imageFound = true;
	}
	else if (links.length > 0) {	//Atom enclosure
	    //Check if the enclosure is an image (Atom allows multiple enclosures per item).
	    linksLoop:
	    for (var j = 0; j < links.length; j++) {
		if (links[j].getAttribute("rel") == "enclosure") {
		    for (var j in this.supportedTypes) {
			if (links[j].getAttribute("type") == this.supportedTypes[j]) {
			    this.urls.push(links[j].getAttribute("href"));
			    imageFound = true;
			    break linksLoop;
			}
		    }
		}
	    }
	}
	
	//Add a null url if this particular entry has no image.
	if (!imageFound) {
	    this.urls.push(null);
	}
    }
}
//Check if this feed is Media RSS and if so, whether or not the current entry has an image associated with it.
RiseVision.RSS.prototype.hasImage = function(entry) {
    if ((entry.mediaGroups) && (entry.mediaGroups.length > 0)) {
	if ((entry.mediaGroups[0].contents) && (entry.mediaGroups[0].contents.length > 0)) {
	    //This is an image if medium="image".
	    if ((entry.mediaGroups[0].contents[0].medium) && (entry.mediaGroups[0].contents[0].medium == "image"))
		return true;
	    //Check if the MIME type is one of the supported image types.
	    else if (entry.mediaGroups[0].contents[0].type) {
		for (var i in this.supportedTypes) {
		    if (entry.mediaGroups[0].contents[0].type == this.supportedTypes[i])
			return true;
		}
	    }
	}
    }
    
    return false;
}
//Preload the images.
RiseVision.RSS.prototype.preloadImages = function() {
    var toLoad = this.urls.length;
    
    this.imageCount = 0;
    this.images = new Array();
    this.loadImage(toLoad);
}
//Load each image.
RiseVision.RSS.prototype.loadImage = function(toLoad) {
    var self = this,
	feedImage;

    //Add a null image if there is no URL.
    if (this.urls[this.imageCount] == null) {
	this.onImageLoaded(null, toLoad);
    }
    else {
	feedImage = new Image();
	feedImage.onload = function() {
	    clearTimeout(self.feedLoadFailedTimer);
	    self.onImageLoaded(feedImage, toLoad);
	}

	//Give the image 5 seconds to load before starting over.
	this.feedLoadFailedTimer = setTimeout(function() {
	    self.feedLoadFailed();
	}, this.feedLoadFailedInterval);
	feedImage.src = this.urls[this.imageCount];
    }
}
RiseVision.RSS.prototype.onImageLoaded = function(image, toLoad) {
    this.images.push(image);
    toLoad--;
    this.imageCount++;

    if (toLoad == 0) {
	this.init();
	this.isFadingIn = false;	//Issue 776
	this.isFadingOut = false;	//Issue 776
	    
	if (this.isLoading) {	    		
	    this.isLoading = false;
	    readyEvent();
	}
	else {
	    this.showEntry();
	}
    }
    else {
	this.loadImage(toLoad);
    }
}
RiseVision.RSS.prototype.showImage = function($text, image) {			
    var size = prefs.getFloat("mediaSize"),
	position = prefs.getString("mediaPlacement"),
	$image = $("<img>");
    
    //Add an image element and dynamically position it.
    $image.attr("id", "image");
    
    //Set the width and size of the image using the mediaSize user preference.
    if (image.width) {
	$image.width(image.width * size);
    }
	    
    if (image.height) {
	$image.height(image.height * size);
    }
						    
    if (image.src) {
	$image.attr("src", image.src);
    }
    
    if ((position == "left") || (position == "right")) {
	$image.attr("align", position);
    }
    
    //Add as last child of parent container.
    if (position == "bottom") {
	$image.insertAfter($text);
    }
    //Add before div containing text.
    else {
	$image.insertBefore($text);
    }		
}
//Handles auto-sizing of the title and text to fit inside the placeholder, if possible.
RiseVision.RSS.prototype.autoSize = function($group) {				
    var fontRatio1, fontRatio2,
	maxFontSize = 64,
	defaultFontSize = 16,
	width = prefs.getInt("rsW"),
	height = prefs.getInt("rsH"),
	titleFontSize = parseInt($(".title_font-style:last").css("font-size")),
	textFontSize =  parseInt($(".text_font-style:last").css("font-size")),
	authorFontSize =  parseInt($(".author_font-style:last").css("font-size")),
	showTitle = prefs.getBool("showTitle"),
	showAuthor = (prefs.getString("showAuthor") != "none");
    
    $("#autoSize").empty();
    $("#autoSize").append($group.clone());
    //Reset positioning (only necessary for slide transition).
    $("#autoSize .group").css("position", "static");
    
    if (!showTitle) {
	titleFontSize = -1;
    }
    
    if (!showAuthor) {
	authorFontSize = -1;
    }
    
    //Maintain relative font sizes.
    if (titleFontSize >= textFontSize && titleFontSize >= authorFontSize) {
	fontRatio1 = titleFontSize / textFontSize;
	fontRatio2 = titleFontSize / authorFontSize;
	titleFontSize = maxFontSize;
	textFontSize = Math.round(titleFontSize / fontRatio1);
	authorFontSize = Math.round(titleFontSize / fontRatio2);
    }
    else if (textFontSize >= titleFontSize && textFontSize >= authorFontSize) {
	fontRatio1 = textFontSize / titleFontSize;
	fontRatio2 = textFontSize / authorFontSize;
	textFontSize = maxFontSize;
	titleFontSize = Math.round(textFontSize / fontRatio1);
	authorFontSize = Math.round(textFontSize / fontRatio2);
    }
    else {
	fontRatio1 = authorFontSize / titleFontSize;
	fontRatio2 = authorFontSize / textFontSize;
	authorFontSize = maxFontSize;
	titleFontSize = Math.round(authorFontSize / fontRatio1);
	textFontSize = Math.round(authorFontSize / fontRatio2);
    }

    if (titleFontSize > 0) {
	$("#autoSize .title_font-style").css("font-size", titleFontSize);
    }
	
    if (authorFontSize > 0) {
	$("#autoSize .author_font-style").css("font-size", authorFontSize);
    }
	
    $("#autoSize .text").css("font-size", textFontSize);
    $("#autoSize").css("width", width);
    $("#autoSize .group").show();

    //Continually decrease the font sizes until the content fits, or until we reach a minimum font size of 12.
    while ((document.getElementById("autoSize").offsetHeight > height) && (textFontSize > 12)) {
	if (showAuthor) {
	    if (authorFontSize > 12) {
		$("#autoSize .author_font-style").css("font-size", --authorFontSize);
	    }
	    else {
		break;
	    }
	}
	
	if (showTitle) {
	    if (titleFontSize > 12) {
		$("#autoSize .title_font-style").css("font-size", --titleFontSize);
	    }
	    else {
		break;
	    }
	}
	
	$("#autoSize .text").css("font-size", --textFontSize);
    }
    
    if (showTitle) {
	$group.find(".title_font-style").css("font-size", $("#autoSize .title").css("font-size"));
    }
	
    if (showAuthor) {
	$group.find(".author_font-style").css("font-size", $("#autoSize .author").css("font-size"));
    }
    
    $group.find(".text").css("font-size", $("#autoSize .text").css("font-size"));
}
RiseVision.RSS.prototype.transitionOut = function() {
    var self = this,
	$visible = $(".visible"),
	transition = prefs.getString("transition");

    if (transition == "fade") {
	if (!this.isFadingIn && !this.isFadingOut && ($visible.length > 0)) {
	    this.showFeedTimer = null;
	    this.isFadingOut = true;
	    
	    //fadeout doesn't always fire callback as I've seen multiple entries with visible class.	
	    //If that's the case though, how does the next entry get shown?
	    //Try kicking off all entries except the first visible one off-screen.
	    $visible.fadeOut("slow", function() {
		self.isFadingOut = false;
		
		//If the feed is in the process of reloading, then don't bother setting visibility,
		//since this will be taken care of once the feed has loaded.
		if (self.isFeedLoaded) {
		    $visible.removeClass("visible");
		    $visible.css("display", "none");
		    
		    if (!self.isPaused) {
			self.showEntry();
		    }
		}
	    });
	}
    }
    else {	//Slide
	this.showFeedTimer = null;
	
	if (this.isPaused) {
	    this.timeLeft = this.duration;
	}
	
	this.showEntry();
    }
}
RiseVision.RSS.prototype.startTimer = function() {
    var self = this;
    
    //Ensure timer is cleared before starting it again.
    clearTimeout(this.showFeedTimer);
    this.showFeedTimer = setTimeout(function() {
	self.transitionOut();
    }, this.duration);
    
    if (!this.isPaused) {
	this.startTimeout = new Date();
    }
}
RiseVision.RSS.prototype.pauseTimer = function() {
    var seconds;
    
    if (this.isPlaying) {
	clearTimeout(this.showFeedTimer);
	this.timeLeft = this.duration;
	this.timeLeft -= new Date() - this.startTimeout;
	
	//Round to nearest second, since duration is counted in seconds.
	seconds = Math.round(this.timeLeft / 1000);
	this.timeLeft = seconds * 1000;
	
	//Show next entry.
	if (seconds <= 0) {
	    this.timeLeft = 0;
	    this.transitionOut();			
	}
    }
}
RiseVision.RSS.prototype.resumeTimer = function() {
    var self = this;
    
    if (this.timeLeft == 0) {
	this.timeLeft = this.duration;
	
	if (this.isFeedLoaded) {
	    this.showEntry();
	}
	else {
	    this.loadFeed();
	}
	
	return;
    }
    
    //Ensure timer is cleared before starting it again.
    clearTimeout(this.showFeedTimer);
    this.showFeedTimer = setTimeout(function() {
	self.transitionOut();
    }, this.timeLeft);
}
RiseVision.RSS.prototype.play = function() {
    this.isPlaying = true;
    this.isPaused = false;
    this.resumeTimer();
}
RiseVision.RSS.prototype.pause = function() {
    var transition = prefs.getString("transition");
		
    this.isPaused = true;

    if (transition == "fade") {
	this.pauseTimer();
    }
    else if ((transition == "slide") && this.showFeedTimer) {	//Timer is set but was interrupted.
	this.pauseTimer();
    }
}